﻿// ==================================================================================================
// Copyright(C) 2012 NCS Pte. Ltd. All rights reserved.
//
// SYSTEM NAME	    : iConnect .Net Application Framework
// COMPONENT ID	    : NCS.IConnect.Architecture.Client
// COMPONENT DESC   : The foundation component of the whole framework. 	
//
// CREATED DATE/BY  : 13 Apr 2012 / Wu Yong Qing
//
// REVISION HISTORY :	
// DATE/BY			SR#/CS/PM#/OTHERS		DESCRIPTION OF CHANGE
// ==================================================================================================

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.ServiceModel;
using NCS.IConnect.ExceptionHandling.Wcf;

namespace NCS.IConnect.Core.IoC.TransientCall
{
    /// <summary>
    /// Default channel provider.
    /// </summary>
    public class DefaultChannelProvider: IInstanceProvider
    {
        private static readonly Dictionary<string, object> channelFactories = new Dictionary<string, object>();
        private static object syncHelper = new object();

        /// <summary>
        /// Initializes a new instance of the <see cref="DefaultChannelProvider"/> class.
        /// </summary>
        /// <param name="contractType">Type of the contract.</param>
        /// <param name="endpointName">Name of the endpoint.</param>
        public DefaultChannelProvider(Type contractType, string endpointName)
        {
            this.TargetType = contractType;
            this.EndpointName = endpointName;
        }

        /// <summary>
        /// Gets the type of the target.
        /// </summary>
        /// <value>
        /// The type of the target.
        /// </value>
        public Type TargetType { get; private set; }

        /// <summary>
        /// Gets the name of the endpoint.
        /// </summary>
        /// <value>
        /// The name of the endpoint.
        /// </value>
        public string EndpointName { get; private set; }

        /// <summary>
        /// Gets the current instance.
        /// </summary>
        public object Current { get; private set; }

        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <returns></returns>
        public object GetInstance()
        {
            var channelFactory = GetChannelFactory(this.TargetType, this.EndpointName);
            this.Current = typeof(ChannelFactory<>).MakeGenericType(this.TargetType).GetMethod("CreateChannel", Type.EmptyTypes).Invoke(channelFactory, new object[] { });
            return Current;
        }

        private object GetChannelFactory(Type serviceContractType, string endpointName)
        {
            object channelFactory;
            if (channelFactories.TryGetValue(endpointName, out channelFactory))
            {
                return channelFactory;
            }

            lock(syncHelper)
            {
                if (channelFactories.TryGetValue(endpointName, out channelFactory))
                {
                    return channelFactory;
                }
                Type channelFactoryType = typeof(ExceptionHandlingChannelFactory<>).MakeGenericType(serviceContractType);
                channelFactory = channelFactoryType.GetConstructor(new[] { typeof(string) }).Invoke(new[] { endpointName });
                channelFactories[endpointName] = channelFactory;
                return channelFactory;
            }
        }
    }
}
